module RbNarcissus
  class SearchableArray < Array
    attr_accessor :capture
    attr_accessor :capture_accumulator, :capture_reset_accumulator

    def initialize *args
      args.each_with_index do |a, i|
        self[i] = a
      end
    end

    # Expects that all items in search array are found in self
    def search search_array
      # puts "\nattempting to match #{search_array.inspect} with #{self.inspect}"
      unless search_array.kind_of?(Array)
        contained = self.search [ search_array ]
        return false unless contained
        return contained.kind_of?(Array) ? contained.first : contained
      end
      matchable_elements = self.dup

      # Before attempting to match each item of the search array with this array,
      # attempt to match the entire search array with this array.
      # aggressive_match = matchable_elements.map do |element|
      #   case element
      #   when SearchableHash, SearchableArray
      #     element.search search_array
      #   else
      #     element == search_array
      #   end
      # end
      # 
      # # puts "\n\n"
      # # puts "aggressive match: #{aggressive_match.inspect}"
      # return self unless aggressive_match.include?(false)

      matched_items = search_array.map do |element|
        match = false
        begin
          matchable_element = matchable_elements.slice!(0)
          match = case matchable_element
          when SearchableArray, SearchableHash
            matchable_element.capture_accumulator = self.capture_accumulator
            matchable_element.capture_reset_accumulator = self.capture_reset_accumulator
            matchable_element.search element
          else
            matchable_element == element ? element : false
          end
          # puts "#{element.inspect} #{match ? 'matches' : 'does not match'} #{matchable_element.inspect}"
        end while !match && !matchable_elements.empty?
        match
      end

      # puts "matched items #{matched_items.inspect}"

      # No misses allowed
      if !matched_items.include?(false)
        return (matched_items.size == 1 ? matched_items.first : matched_items)
      else
        return false
      end
    end
  end

  class SearchableHash < Hash
    attr_accessor :capture
    attr_accessor :capture_accumulator, :capture_reset_accumulator

    def initialize source_hash = nil
      source_hash.each do |k,v|
        self[k] = v
      end if source_hash
    end

    def / *args
      self.search *args
    end

    def search search_expression
      if search_expression.kind_of?(Hash) && search_expression.keys.empty?
        puts "I should be captured #{self.inspect}" if search_expression.kind_of?(SearchableHash) && search_expression.capture
        return self
      end

      if search_expression.kind_of?(Hash) && (search_expression.keys - keys).empty?
        # This hash has all the keys requested by this level of the search expression.
        # Do all the keys satisfy?
        # inject_match = search_expression.inject(true) do |b, h|
        #   key, value = h
        #   item_matches_children = (
        #     if self[key].kind_of?(value.class)
        #       case self[key]
        #       when SearchableHash, SearchableArray
        #         self[key].capture_accumulator = self.capture_accumulator
        #         self[key].capture_reset_accumulator = self.capture_reset_accumulator
        #         self[key].search value
        #       else
        #         self[key] == value
        #       end
        #     else
        #       false
        #     end
        #   )
        #   item_matches_children
        # end

        matched = search_expression.map do |key, value|
          item_matches_children = (
            if self[key].kind_of?(value.class)
              case self[key]
              when SearchableHash, SearchableArray
                self[key].capture_accumulator = self.capture_accumulator
                self[key].capture_reset_accumulator = self.capture_reset_accumulator
                self[key].search value
              else
                self[key] == value
              end
            else
              false
            end
          )
        end
        if matched && !matched.empty? && !matched.include?(false)
          if search_expression.kind_of?(SearchableHash) && search_expression.capture
            # puts "\n\nI should be captured #{self.inspect} because I match #{search_expression.inspect}"
            search_expression.capture = false
            capture_accumulator << self if capture_accumulator
            # puts "capture_accumulator: #{capture_accumulator.inspect}"
            capture_reset_accumulator << search_expression if capture_reset_accumulator
          else
            # puts "returning #{self.inspect} but not capturing"
          end
          return self
        end
      end

      matching_nodes = self.map do |key, value|
        case value
        when SearchableHash, SearchableArray
          value.capture_accumulator = self.capture_accumulator
          value.capture_reset_accumulator = self.capture_reset_accumulator
          value.search search_expression
        else
          false
        end
      end
      matching_nodes = matching_nodes.delete_if { |e| e == false }

      if matching_nodes.size == 1
        return matching_nodes.first
      elsif matching_nodes.size > 1
        return matching_nodes
      else
        return false
      end
    end
  end

end
